package cn.net.sinodata.common.hazelcast.utils;

import cn.net.sinodata.common.core.constant.CacheNames;
import com.hazelcast.core.HazelcastException;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import cn.net.sinodata.common.core.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

/**
 * hazelcast 分布式缓存工具类，单例工具类，确保工具类全局唯一（慎用缓存）
 *  IMap：分布式Map
 *  cacheName：缓存组名称（如：“sys_config”）
 *  key：缓存键（非空、建议使用字符串或数字）
 *  value：值类型（需实现Serializable）
 *
 * @author yinliangyun
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class HazelcastUtils {

    private static final HazelcastInstance hazelcastInstance = SpringUtils.getBean(HazelcastInstance.class);

    private static Set<String> validCacheNames = new HashSet<>();

    /**
     * 获取客户端实例
     */
    public static HazelcastInstance getHazelcastInstance() {
        return hazelcastInstance;
    }

    // ------------------------------------------------------分布式缓存------------------------------------------------------

    /**
     * 根据缓存名称获取IMap（带白名单校验）
     *
     * @param cacheName 缓存组名称（必须来自CacheNames接口常量）
     * @param <K>       键类型（非空）
     * @param <V>       值类型（需实现Serializable）
     * @return 分布式Map实例
     * @throws IllegalArgumentException 缓存名非法/空
     * @throws HazelcastException        Hazelcast实例获取失败
     */
    public static <K, V> IMap<K, V> getMap(String cacheName) {
        // 执行 cacheName 校验
        validateCacheName(cacheName);

        log.debug("getMap success: cacheName={}", cacheName);
        return hazelcastInstance.getMap(cacheName);
    }

    /**
     * 通用添加缓存方法，无需设置过期时间
     *
     * @param cacheName 缓存组名称（来自CacheNames）
     * @param key       缓存键（非空）
     * @param value     缓存值（可null，非null时需实现Serializable）
     * @return true：添加成功；false：添加失败
     */
    public static <K, V> boolean addCache(String cacheName, K key, V value) {
        // 1. 基础校验（键非空）
        if (key == null) {
            log.error("addCache failed: key is null, cacheName={}", cacheName);
            return false;
        }

        // 2. 序列化校验（非null值需实现Serializable）
        if (value != null && !isSerializable(value)) {
            log.error("addCache failed: value is not serializable, cacheName={}, key={}", cacheName, key);
            return false;
        }

        IMap<K, V> map = null;
        try {
            // 3. 获取缓存实例
            map = getMap(cacheName);
            log.info("addCache start: cacheName={}, key={}, value={}", cacheName, key, value);
            map.put(key, value);
            log.info("addCache success: cacheName={}, key={}", cacheName, key);
            return true;
        } catch (IllegalArgumentException e) {
            log.error("addCache failed: invalid cacheName", e);
            return false;
        } catch (HazelcastException e) {
            // 捕获Hazelcast特有异常（序列化失败、分区丢失、连接超时等）
            log.error("addCache failed: Hazelcast error, cacheName={}, key={}", cacheName, key, e);
        } catch (Exception e) {
            log.error("addCache failed: unexpected error, cacheName={}, key={}", cacheName, key, e);
        }
        return false;
    }

    /**
     * 通用获取缓存方法
     *
     * @param cacheName 缓存组名称（来自CacheNames）
     * @param key       缓存键（非空）
     * @return 缓存值（null表示“键不存在”或“操作失败”，可通过日志区分）
     */
    public static <K, V> V getCache(String cacheName, K key) {
        // 1. 基础校验
        if (key == null) {
            log.error("getCache failed: key is null, cacheName={}", cacheName);
            return null;
        }

        try {
            // 2. 获取缓存实例并查询
            IMap<K, V> map = getMap(cacheName);
            log.debug("getCache start: cacheName={}, key={}", cacheName, key);
            V value = map.get(key);
            log.debug("getCache success: cacheName={}, key={}, value={}", cacheName, key, value);
            return value;
        } catch (IllegalArgumentException e) {
            log.error("getCache failed: invalid cacheName", e);
            return null;
        } catch (HazelcastException e) {
            log.error("getCache failed: Hazelcast error, cacheName={}, key={}", cacheName, key, e);
        } catch (Exception e) {
            log.error("getCache failed: unexpected error, cacheName={}, key={}", cacheName, key, e);
        }
        return null;
    }

    /**
     * 通用删除缓存中单个键值对方法
     *
     * @param cacheName 缓存组名称（来自CacheNames）
     * @param key       缓存键（非空）
     * @return 被删除的值（null表示“键不存在”或“操作失败”，可通过日志区分）
     */
    public static <K, V> V removeCache(String cacheName, K key) {
        // 1. 基础校验
        if (key == null) {
            log.error("removeCache failed: key is null, cacheName={}", cacheName);
            return null;
        }

        try {
            // 2. 获取缓存实例并删除
            IMap<K, V> map = getMap(cacheName);
            log.info("removeCache start: cacheName={}, key={}", cacheName, key);
            V removedValue = map.remove(key);
            return removedValue;
        } catch (IllegalArgumentException e) {
            log.error("removeCache failed: invalid cacheName", e);
            return null;
        } catch (HazelcastException e) {
            log.error("removeCache failed: Hazelcast error, cacheName={}, key={}", cacheName, key, e);
        } catch (Exception e) {
            log.error("removeCache failed: unexpected error, cacheName={}, key={}", cacheName, key, e);
        }
        return null;
    }

    /**
     * 通用清空缓存组方法（清空指定缓存组下的所有键值对）
     *
     * @param cacheName 缓存组名称（来自CacheNames）
     * @return true：清空成功；false：清空失败（参数无效/缓存操作异常）
     */
    public static <K, V> boolean clearCache(String cacheName) {
        try {
            // 获取缓存实例并执行清空（同步清空，保证集群数据一致）
            IMap<K, V> map = getMap(cacheName);
            log.info("clearCache start: 开始清空缓存组，cacheName={}", cacheName);
            map.clear(); // 核心：调用Hazelcast IMap的clear()方法，清空所有键值对
            log.info("clearCache success: 缓存组清空完成，cacheName={}", cacheName);
            return true;
        } catch (IllegalArgumentException e) {
            log.error("clearCache failed: 无效的缓存组名称，cacheName={}", cacheName, e);
            return false;
        } catch (HazelcastException e) {
            log.error("clearCache failed: Hazelcast集群操作异常，cacheName={}", cacheName, e);
            return false;
        } catch (Exception e) {
            log.error("clearCache failed: 未知异常，cacheName={}", cacheName, e);
            return false;
        }
    }

    // ------------------------------------------------------ 私有辅助方法 ------------------------------------------------------

    static {
        getCacheNameWhitelist();
        log.info("getCacheNameWhitelist success validCacheNames:{}", validCacheNames);
    }

    /**
     * 缓存名白名单校验（基于CacheNames接口反射获取常量）
     */
    private static void validateCacheName(String cacheName) {
        // 1. 非空校验
        if (cacheName == null) {
            throw new IllegalArgumentException("缓存名 cacheName 不能为空！");
        }

        // 2. 非空白校验（去除首尾空格）
        String trimmedName = cacheName.trim();
        if (StringUtils.isEmpty(trimmedName)) {
            throw new IllegalArgumentException("缓存名 cacheName 不能为空白字符（如空格、空字符串）！");
        }

        // 3. 白名单校验
        if (!validCacheNames.contains(trimmedName)) {
            throw new IllegalArgumentException(
                String.format("缓存名 cacheName 非法！仅允许使用预定义常量：%s，当前传入：%s",
                    validCacheNames, cacheName)
            );
        }
    }

    /**
     * 反射获取CacheNames接口中所有public static final的String常量（自动维护白名单）
     */
    private static void getCacheNameWhitelist() {
        Field[] fields = CacheNames.class.getDeclaredFields();
        for (Field field : fields) {
            // 筛选：public static final + String类型
            if (isPublicStaticFinal(field) && field.getType() == String.class) {
                try {
                    String cacheName = (String) field.get(null); // 静态字段无需实例
                    validCacheNames.add(cacheName);
                } catch (IllegalAccessException e) {
                    log.error("获取CacheNames常量失败！字段名：{}", field.getName(), e);
                    throw new RuntimeException("缓存名白名单初始化失败", e);
                }
            }
        }
    }

    /**
     * 判断字段是否为public static final
     */
    private static boolean isPublicStaticFinal(Field field) {
        int modifiers = field.getModifiers();
        return Modifier.isPublic(modifiers)
            && Modifier.isStatic(modifiers)
            && Modifier.isFinal(modifiers);
    }

    /**
     * 校验对象是否可序列化（包括数组、集合等）
     */
    private static boolean isSerializable(Object obj) {
        if (obj == null) {
            return true; // null无需序列化
        }
        // 基本类型及其包装类、String默认可序列化
        if (obj instanceof Serializable) {
            return true;
        }
        // 数组：需判断元素类型是否可序列化
        if (obj.getClass().isArray()) {
            Class<?> componentType = obj.getClass().getComponentType();
            return componentType.isPrimitive() || Serializable.class.isAssignableFrom(componentType);
        }
        return false;
    }

}
